/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://code.google.com/p/dylab/

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Math_h__
#define __dylab_Math_h__

//===========================================================================================
#include <math.h>

//===========================================================================================
#include <dylab/utils/String.h>
#include <dylab/utils/random/RandomReal.h>

//===========================================================================================
namespace dylab {

    // undef min & max macros
#   ifdef max    
#       undef max
#       pragma message("dylab: max undefined")
#   endif
#   ifdef min
#       undef min
#       pragma message("dylab: min undefined")
#   endif

    /**
     */    
    class DYLAB_EXPORT Math
    {    
    public:
        ///
        enum pointcls_t
        {
            POINTCLS_INNER,
            POINTCLS_VERTEX,
            POINTCLS_EDGE,
            POINTCLS_FACE,
            POINTCLS_OUTER
        };
        ///
        enum intersection_t
        {
            INTERSECTION_NONE,
            INTERSECTION_POINT,
            INTERSECTION_LINE
        };
    public:
        /// zero and unit real constants
        static const real_t ZERO;
        static const real_t ONE;
        static const real_t PI;
        ///
        static const real_t EQUALITY_TOLERANCE;
    private:
        ///
        static RandomReal s_mRealGenerator;
    public:

        /**
        */
        static real_t random()
                { return s_mRealGenerator.generate(); }

        /**
        */
        static real_t abs(real_t _x)
                { return _x < ZERO ? -_x : _x; }

        /**
        */
        static real_t sign(real_t _x, real_t _tolerance = EQUALITY_TOLERANCE)
                { return (_x <= _tolerance) ? ((_x < -_tolerance) ? -ONE : ZERO) : ONE; }

        /**
        */
        static real_t min(real_t _x, real_t _y)
                { return _x < _y ? _x : _y; }

        /**
        */
        static real_t max(real_t _x, real_t _y)
                { return _x > _y ? _x : _y; }

        /**
        */
        static real_t clamp(real_t _value, real_t _min, real_t _max)
                { return min(_max, max(_value, _min)); }

        /**
        */
        static real_t squareRoot(real_t _x)
                { return sqrt(_x); }

        /**
        */
        static bool almostEqual(real_t _x, real_t _y, real_t _tolerance = EQUALITY_TOLERANCE)
                { return abs(_x - _y) <= _tolerance; }

        /**
        */
        static bool almostZero(real_t _x, real_t _tolerance = EQUALITY_TOLERANCE)
                { return almostEqual(_x, ZERO, _tolerance); }

        /**
        */
        static bool almostOne(real_t _x, real_t _tolerance = EQUALITY_TOLERANCE)
                { return almostEqual(_x, ONE, _tolerance); }

        /**
        */
        static real_t toRadians(real_t _degrees)
                { return _degrees * PI / DYLAB_R(180); }

        /**
        */
        static real_t toDegrees(real_t _radians)
                { return _radians * DYLAB_R(180) / PI; }

        /** TODO: move to Matrix3x3
         */
        static real_t computeDeterminant3x3(real_t a1, real_t a2, real_t a3, real_t b1, real_t b2, real_t b3, real_t c1, real_t c2, real_t c3)
                { return (a1*b2*c3)-(a1*b3*c2)-(a2*b1*c3)+(a3*b1*c2)+(a2*b3*c1)-(a3*b2*c1); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const Math::pointcls_t & _o)
            { 
                switch (_o)
                {
                case Math::POINTCLS_VERTEX:
                    return _os << "POINTCLS_VERTEX";
                case Math::POINTCLS_EDGE:
                    return _os << "POINTCLS_EDGE"; 
                case Math::POINTCLS_FACE:
                    return _os << "POINTCLS_FACE"; 
                case Math::POINTCLS_INNER:
                    return _os << "POINTCLS_INNER"; 
                case Math::POINTCLS_OUTER:
                    return _os << "POINTCLS_OUTER"; 
                default:
                    return _os;
                }
            }
}

//===========================================================================================
#endif // __dylab_Math_h__
